package com.nulldev.util.web.nTube.pages.misc;

import java.io.IOException;

import com.nulldev.util.VariableAPI.RandomUtil;
import com.nulldev.util.concurrency.threadIt.v4.emapi.ExecutorManager;
import com.nulldev.util.languages.json.uniapi.UniJsonValue;
import com.nulldev.util.networking.misc.SocketUtil;
import com.nulldev.util.web.HttpServer3.extensions.websocket.Peer;
import com.nulldev.util.web.HttpServer3.extensions.websocket.WebSocketPage;
import com.nulldev.util.web.HttpServer3.extensions.websocket.util.CloseReason.CloseCodes;

public class NetworkThroughputTestPage extends WebSocketPage {

	public NetworkThroughputTestPage() {
		super("/api/network/throughput_test_ws", "ntube_throughput");
	}

	@Override
	public void onTextMessage(final Peer peer, final String message) {
		/* Reject blank messages. */
		if (message == null || message.length() == 0) {
			peer.sendCloseQuietly(CloseCodes.VIOLATED_POLICY);
			return;
		}
		/* Reject non-JSON messages. */
		if (!UniJsonValue.probablyJSON(message)) {
			peer.sendCloseQuietly(CloseCodes.VIOLATED_POLICY);
			return;
		}
		final UniJsonValue msg = UniJsonValue.parse(message);
		switch (msg.getString("type")) {
			case "ntube-start-throughput-test": {
				final UniJsonValue _resp = UniJsonValue.object(5);
				_resp.add("type", "test-info");
				/* Debugging Info. */
				_resp.add("tcp_rbuf", SocketUtil.getRecieveBufferSize(peer.getSocket()));
				_resp.add("tcp_sbuf", SocketUtil.getSendBufferSize(peer.getSocket()));
				/* Versioning. */
				_resp.add("version", "2023-11-14");
				/* Timestamp. */
				_resp.add("timestamp", System.currentTimeMillis());
				try {
					peer.write(_resp.toString());
				} catch (IOException e) {
					peer.sendCloseQuietly(CloseCodes.PROTOCOL_ERROR);
				}
				__queue_peer_for_test(peer, !peer.getHeaders().containsKey("Save-Data"));
				break;
			}
			default: {
				peer.sendCloseQuietly(CloseCodes.VIOLATED_POLICY);
				break;
			}
		}
	}

	@Override
	public void onBinaryMessage(final Peer peer, final byte[] message) {
		/* Not permitted, drop connection. */
		try {
			peer.sendClose(CloseCodes.CANNOT_ACCEPT);
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}

	@Override
	public void peerConnected(final Peer peer) {
		// NO-OP
	}

	@Override
	public void peerDisconnected(final Peer peer) {
		// NO-OP
	}

	private static final int[] REGULAR_CHUNK_SIZES = new int[]
		{ (4 * 1024), (64 * 1024), (128 * 1024), (256 * 1024), (512 * 1024), };

	private static final int[] LARGE_CHUNK_SIZES = new int[]
		{ (1 * 1024 * 1024), (2 * 1024 * 1024), (4 * 1024 * 1024), };

	private static void __queue_peer_for_test(final Peer peer, final boolean __allow_large_chunks) {
		ExecutorManager.globalAsyncSubmit(() -> {
			__test: {
				for (final int regular_chunk : REGULAR_CHUNK_SIZES) {
					final byte[] data = RandomUtil.bytes(regular_chunk);
					final long startAt = System.currentTimeMillis();
					try {
						peer.write(data);
						final UniJsonValue _resp = UniJsonValue.object(3);
						_resp.add("type", "test-chunk");
						_resp.add("timestamp", startAt);
						_resp.add("size", regular_chunk);
						peer.write(_resp.toString());
					} catch (IOException _ex) {
						break __test;
					}
				}
				if (__allow_large_chunks) {
					for (final int regular_chunk : LARGE_CHUNK_SIZES) {
						final byte[] data = RandomUtil.bytes(regular_chunk);
						final long startAt = System.currentTimeMillis();
						try {
							peer.write(data);
							final UniJsonValue _resp = UniJsonValue.object(3);
							_resp.add("type", "test-chunk");
							_resp.add("timestamp", startAt);
							_resp.add("size", regular_chunk);
							peer.write(_resp.toString());
						} catch (IOException _ex) {
							break __test;
						}
					}
				}
			}
			peer.sendCloseQuietly();
			return null;
		});
	}
}
